Skip to main content

An EVM Developer's Guide to Solana Proving

Ethereum (EVM) Proof System

In Ethereum, you have:

  • State Root: Merkle root of the entire state trie at a given block
  • Receipt Root: Merkle root of all transaction receipts in a block
  • Merkle Proofs: You can prove inclusion of any state or event by providing a merkle path

This allows for stateless verification - you can prove something happened without having the full state.

Solana (SVM) Proof System

Solana deliberately avoids merkleization for performance reasons, but provides proofs through different mechanisms:

1. Account State Proofs

Instead of a global state root, Solana uses:

  • Account Hash: Each account has a hash based on its data, lamports, owner, etc.
  • Bank Hash: A hash of all account states at a given slot
  • Accounts Delta Hash: Hash of state changes within a slot

2. Transaction Confirmation Proofs

For proving transactions occurred:

  • Transaction Signatures: Cryptographic proof the transaction was included
  • Slot Confirmation: Validators attest to the slot's validity
  • Vote Records: Validator consensus proofs stored on-chain

3. Program Log Verification

For events/logs (equivalent to Ethereum events):

  • Program Logs: Stored in transaction metadata
  • Return Data: Programs can return data that gets included in transaction results
  • Account State Changes: Before/after snapshots of affected accounts
info

Polymer prioritizes event proofs for EVM chains, given their developer experience and proving costs. For consistency, we follow a similar approach with Solana by using Program Log verification with Confirmation proofs. (See Trust Considerations next)

How EVM Log Proof Work


Block Header
├── Receipt Root (merkle root of all transaction receipts)

Transaction Receipts Trie
├── Receipt 0
│ ├── status, gasUsed, logsBloom
│ └── logs: [
│ {
│ address: "0x123...",
│ topics: ["0xEventHash", "0xIndexedParam1", ...],
│ data: "0x..."
│ }
│ ]

How SVM Log Proofs Work

Slot N (confirmed slot 2/3+ stake-weighted validators)
├── Block Hash = hash(previous_hash, entries[], accounts_delta_hash)
└── Transaction[i] = {signatures[], message, meta}
└── TransactionMeta = {
err: Option<Error>,
logs: Vec<String>, // ← Program-emitted logs
return_data: Option<Data>, // ← Structured program output
pre_balances: Vec<u64>,
post_balances: Vec<u64>,
pre_token_balances: Vec<TokenBalance>,
post_token_balances: Vec<TokenBalance>
}
└── Log[j] = "Program {program_id} invoke [depth]" |
"Program log: {custom_message}" |
"Program {program_id} success/failed"